//! 加签工具集 椭圆曲线加签和验签

use crate::{
    error::{Error, ErrorKind},
    wallet,
};
use base64::Engine;
use crypto::{
    digest::Digest,
    sha3::{Sha3, Sha3Mode},
};
use hdwallet::secp256k1::{ecdsa::Signature, Message, PublicKey, Secp256k1, SecretKey};
use serde::Deserialize;
use serde_json::{json, Value};

pub fn sign(message: &str, private_key: &SecretKey) -> Result<String, Error> {
    let ob = json!(message);
    let source = get_source_string(&ob)?;
    Ok(sign_str(&source, private_key))
}

/// 对字符串进行签名，生成字符串返回
///
pub fn sign_str(msg: &str, private_key: &SecretKey) -> String {
    let hash_bytes = sha3_msg(msg);
    let sign = Secp256k1::new().sign_ecdsa(&Message::from_slice(&hash_bytes).unwrap(), private_key);
    let sign_bytes = sign.serialize_compact();
    base64::engine::general_purpose::STANDARD.encode(sign_bytes)
}

/// 验签， 参数是一个消息的json形式.
pub fn verify_with_msg(msg: &str) -> Result<(), Error> {
    let obj: Msg = serde_json::from_str(msg)?;
    let pubkey = obj.pubkey;
    // 对地址进行验证
    let check_ob = json!(["sig", { "pubkey": pubkey }]);
    let address = wallet::inve_wallet::InveWallet::get_inve_address_by_definition(&check_ob)?;
    if &obj.from_address != &address {
        return Err(ErrorKind::Msg("address error!".to_string()).into());
    }
    let signature = obj.signature;
    let pubkey = base64::engine::general_purpose::STANDARD.decode(pubkey)?;
    let mut ob = json!(msg);
    let map_obj = ob.as_object_mut().expect("msg must be object Value");
    let _ = map_obj.remove("signature");
    let _ = map_obj.remove("hash");
    let source = get_source_string(&ob)?;
    verify_str_with_pub_byte(&source, &signature, &pubkey)
}

/// 验签，参数是一个json的对象格式
pub fn verify_with_obj(ob: &mut Value) -> Result<(), Error> {
    let msg: Msg = serde_json::from_value(ob.clone())?;
    let pubkey = msg.pubkey;
    let check_ob = json!(["sig", { "pubkey": pubkey }]);
    let address = wallet::inve_wallet::InveWallet::get_inve_address_by_definition(&check_ob)?;
    if &msg.from_address != &address {
        return Err(ErrorKind::Msg("address error!".to_string()).into());
    }
    let signature = msg.signature;
    let pubkey = base64::engine::general_purpose::STANDARD.decode(pubkey)?;
    let map_obj: &mut serde_json::Map<String, Value> = ob.as_object_mut().expect("must be object");
    let _ = map_obj.remove("eHash");
    let _ = map_obj.remove("hash");
    let _ = map_obj.remove("id");
    let _ = map_obj.remove("is_stable");
    let _ = map_obj.remove("signature");
    let _ = map_obj.remove("update_time");
    let source = get_source_string(&ob)?;
    verify_str_with_pub_byte(&source, &signature, &pubkey)
}

/// 验签，data是纯数据
pub fn verify_str(data: &str, signature: &str, public_key: &PublicKey) -> Result<(), Error> {
    // 对msg进行sha3操作
    let hash_value = sha3_msg(data);
    // 求出signature的bytes
    let signature_byte = base64::engine::general_purpose::STANDARD.decode(signature)?;
    Ok(Secp256k1::new().verify_ecdsa(
        &Message::from_slice(&hash_value).unwrap(),
        &Signature::from_compact(&signature_byte)?,
        public_key,
    )?)
}

/// 验签， 这里的公钥是字节的形式
pub fn verify_str_with_pub_byte(
    msg: &str,
    signature: &str,
    public_key: &[u8],
) -> Result<(), Error> {
    let public_key = PublicKey::from_slice(public_key)?;
    let msg_hash = sha3_msg(msg);
    let sig_byte = base64::engine::general_purpose::STANDARD.decode(signature)?;
    Ok(Secp256k1::new().verify_ecdsa(
        &Message::from_slice(&msg_hash).unwrap(),
        &Signature::from_compact(&sig_byte)?,
        &public_key,
    )?)
}

/// 获取unit源数据
pub fn get_source_string(ob: &Value) -> Result<String, Error> {
    let mut list = vec![];
    extract_components(ob, &mut list)?;
    Ok(list.join(""))
}

/// 把一些json转变成定义的字符串形式
pub fn extract_components(ob: &Value, list: &mut Vec<String>) -> Result<(), Error> {
    if ob.is_string() {
        list.push("s".to_string());
        list.push(ob.as_str().unwrap().to_string());
    } else if ob.is_array() {
        list.push("[".to_string());
        let arr = ob.as_array().unwrap();
        for v in arr {
            extract_components(v, list)?;
        }
        list.push("]".to_string())
    } else if ob.is_object() {
        let o = ob.as_object().unwrap();
        let mut keys = o.keys().collect::<Vec<_>>();
        keys.sort();
        for k in keys {
            list.push(k.to_string());
            extract_components(o.get(k).unwrap(), list)?;
        }
    } else if ob.is_number() {
        list.push("n".to_string());
        list.push(ob.to_string());
    } else if ob.is_boolean() {
        list.push("b".to_string());
        list.push(ob.as_bool().unwrap().to_string());
    } else {
        return Err(ErrorKind::Msg("extract_components unsupported type".to_string()).into());
    }
    Ok(())
}

#[derive(Deserialize)]
struct Msg {
    pubkey: String,
    from_address: String,
    signature: String,
}

fn sha3_msg(msg: &str) -> [u8; 32] {
    let mut ha = Sha3::new(Sha3Mode::Sha3_256);
    ha.input_str(msg);
    let mut ret = [0u8; 32];
    ha.result(&mut ret);
    ret
}

#[cfg(test)]
mod tests {
    use hdwallet::secp256k1::Secp256k1;

    use crate::{
        mnemonic::Mnemonic,
        utils::sign::{sign_str, verify_str},
    };

    #[test]
    fn sign_test() {
        let m = Mnemonic::new("", "").unwrap();
        let mnemonic = m.mnemonic();
        let seed = m.seed();
        println!("mnemonic: {}, seed: {:?}", mnemonic, seed);
        let extended_private_key = m.master_extended_private();
        // 加签
        let sign_value = sign_str("hello", &extended_private_key.private_key);
        println!("{}", sign_value);
        // 验签
        let public_key = extended_private_key
            .private_key
            .public_key(&Secp256k1::new());

        let ret = verify_str("hello", &sign_value, &public_key);
        if ret.is_err() {
            println!("验签失败");
        } else {
            println!("验签成功");
        }
    }
}
